iT邦幫忙

2023 iThome 鐵人賽

DAY 5
1

I fear not the man who has practiced 10,000 kicks once, but I fear the man who has practiced one kick 10,000 times。 Bruce Lee說過上面這句話,賣由翁也說了「我亦無他,惟手熟爾」。

對於初次接觸新知識或踏入新領域的人來說,無論多新手,都需要從基礎開始學習。勤奮可以彌補不足,但有系統、有組織的學習方式能夠事半功倍。(如果是大神前被發現內容有誤,還請批評指教orz)
期許筆者能有系統的分享,並和讀者一起受惠。

About Data

Make & New

什麼時候會使用到 make 以及 new ?
https://ithelp.ithome.com.tw/upload/images/20230920/20148157GroiigOgj5.png

Make

目的是幫助 slicemapchannel 分配記憶體並返回一個初始化的值。簡單來說就是返回已初始化得數據結構。 而且 用來分配初始化型別為此三的資料。

  1. Slice:返回的總長度是 指定切片長度
// 長度為 5,容量為 10 型別為 int 的 slice
s := make([]int, 5, 10)
a := make(型別, 指定切片長度, 指定預留的空間長度)
  1. Map:只傳型別。
// 返回 string 和 int 的 map
m := make(map[string]int)
  1. Channel:需要指定型別和容量; 指定長度。
// 容量為 10 型別為 int 的 channel
ch := make(chan int, 10)

New

不會初始化內存,只會將內存清零。且 new 返回的是 pointer ,像是 *Type

舉例:

使用 new 分配一個新的 int 並初始化為零值(0),然後返回指向該整數的指針。

var p *int
p = new(int)
fmt.Println(*p) 
// 輸出 0,因為整數的零值是 0

使用 new 分配一個新的 string 並初始化為零值(空string""),然後返回指向該 string 的指針 *

var strPtr *string
strPtr = new(string)
fmt.Println(*strPtr)
輸出空 string ""

使用 new 分配一個新的自定義 struct,然後返回指向該對象的指針,該對象的字段將被初始化為各自類型的零值。

type Person struct {
    Name string
    Age  int
}
var personPtr *Person
personPtr = new(Person)
fmt.Println(personPtr.Name) // 輸出空字符串,personPtr.Age 
輸出 0

可以在分配後為指針指向的對象賦值。將整數指針指向的對象的值設置為 42

*p = 42 
fmt.Println(*p) 
輸出 42

設定自定義結構體的字段值。

personPtr.Name = "Alice"
personPtr.Age = 30
fmt.Println(personPtr.Name, personPtr.Age) 
輸出 "Alice 30"

Constructors and composite literals

有時候零值並不足以滿足我們的需求,我們需要一個自定義的初始化函數(constructor)來為變數提供一個特定的初始狀態。同時,我們可以使用複合字面值(composite literals)來簡化對結構體或其他複雜類型的初始化。

舉例:
先初始化和建立 person 的 sruct 。後返回一個指向新創建函數的指針。

package main

type Person struct {
    Name string
    Age  int
}

func NewPerson(name string, age int) *Person {
    return &Person{
        Name: name,
        Age:  age,
    }
}

p := NewPerson("Alice", 30)

Array && Slice

https://ithelp.ithome.com.tw/upload/images/20230920/20148157MQALrUfCco.png

Array

Array 是一種 固定大小連續 的數據結構,用於存儲 相同類型 的元素。陣列的大小在宣告時就已經確定,且無法改變。

  1. 陣列是值: 代表一個陣列賦值給另一個陣列時,會複製它的所有元素。這使得每個陣列都是獨立的,修改一個陣列不會影響其他陣列。
  2. 陣列的傳遞: 當將一個陣列傳遞給一個函數時,函數將接收到該陣列的一個副本,而不是原始陣列的指針。這表示在函數內對陣列的修改不會影響外部的陣列
  3. 陣列大小是型別的一部分: 陣列的大小是陣列類型的一部分。
  4. Array 的大小等於其記憶體內元素的總大小。
package main

import "fmt"

func main() {
    // 宣告兩個不同大小的整數陣列
    var arr1 [5]  int
    var arr2 [10] int

    // 複製 arr1 到 arr2
    arr2 = arr1

    // 修改 arr2 的元素不會影響 arr1
    arr2[0] = 100

    // 輸出 arr1 和 arr2 的內容
    fmt.Println("arr1:", arr1)
    fmt.Println("arr2:", arr2)
}

會出現以下訊息,因為[5]int[10]int不同的型別,就像 intfloat64不同的型別一樣。無法將一個不同型別的值直接賦值給另一個不同型別的值。

cannot use arr1 (variable of type [5]int) as [10]int value in assignment

Slice

剛剛上面提過,Array固定大小連續 的數據結構,而 Slice 可以在程式運行時增加縮小
Go 中較常使用的會是 Slice

  1. 只要不超出底层array的限制,可以在運行時增加或刪除元素。
  2. 零值為 nil 。
  3. 長度不是型別的一部分。
package main

import "fmt"

func main() {
    // 創建一個整數陣列
    arr := [5]int{1, 2, 3, 4, 5}

    // 創建一個 Slice,包含陣列中的前三個元素
    // Slice 由 arr[0:3] 表示,包含 0 到 2 的元素
    slice := arr[0:3]

    fmt.Println("原始陣列:", arr)
    fmt.Println("Slice:", slice)
}
第二行第三個元素: 6
0 2 3 
4 5 6 
7 8 9 

Two-dimensional slices

二維切片是切片的特殊形,它是一個 包含切片的切片,通常用於表示二維數據結構,例如矩陣或表格。 看起來是 [][] 這樣。

package main

import "fmt"

func main() {
    // 創建一個二維 slice
    matrix := [][]int{
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9},
    }

    // 印出二維切片的值
    fmt.Println("第二行第三個元素:", matrix[1][2])

    // 修改元素的值
    matrix[0][0] = 0

    for _, row := range matrix {
        for _, value := range row {
            fmt.Print(value, " ")
        }
        fmt.Println()
    }
}
第二行第三個元素: 6
0 2 3 
4 5 6 
7 8 9 

Maps

  1. 允許我們使用 key 快速存取 Element。
  2. key 應該是 唯一 的,否則找不到對應的 value
  3. keyValue 不需要匹配,可以不同。
map[string][]int
  1. key 應該是可比較類型。slicemapfunction values 為不能比較的型別。
map[int]bool
  1. keyvalue 應該屬於同一類型。

舉例:

func main() {
    // 用前面提過的 make 建立一個 map。
	scores := make(map[string]int)

    // 加入 key 和 value。
	scores["Alice"] = 95
	scores["Bob"] = 88

    // 使用 key (Alice)來呼叫 value (aliceScore) 。 
	aliceScore := scores["Alice"]
	fmt.Println("1.Alice的分数是:", aliceScore)
    
    // 可以使用 delete 刪除 key 和 value。
    delete(scores, "Bob")

    // 檢查 key 是否存在。
    score, exists := scores["Charlie"]
    if exists {
        fmt.Println("2.Charlie的分数是:", score)
    } else {
        fmt.Println("3.Charlie不存在")
    }

    // 使用 for range 走訪 map 裡的 key 和 value。
    for name, score := range scores {
        fmt.Printf("4.%s的分数是:%d\n", name, score)
    }
}
1.Alice的分数是: 95
3.Charlie不存在
4.Alice的分数是:95

Printing

Append

在目標切面中要增加元素可以使用 append。

func append(slice []T, elements ...T) []T
// slice 目標切片,即要向其添加元素的切片。
// elements 是要添加到切片中的元素,可以是一個或多個。
package main

import (
	"fmt"
)

func main() {
    // 型別為 int 的 slice
	slice := []int{1, 2, 3}

	// 使用 append 向 slice 添加一個 element
	slice = append(slice, 4)

	// 添加多個 element
	slice = append(slice, 5, 6, 7)

	fmt.Println(slice) 
}
[1 2 3 4 5 6 7]

碎語

今天做了 makenew ; arrayslice 的簡單介紹與比較,還有聽起來有點陌生的 Constructors and composite literals ,也稍微提了 MapPrintingAppend

因為鐵人賽的緣故,讓我從新學過一遍,這又是另一種感覺,像極了最熟悉的陌生人。
(前言晚點寫


上一篇
04 | 對程式碼理性的 PUA
下一篇
06 | 又回到最初的起點
系列文
Go 語言學習手札30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言